package com.example.ephuizi.stl.util.phong_illumination;

import android.content.Context;
import android.opengl.GLES20;
import android.opengl.GLSurfaceView;
import android.opengl.Matrix;
import android.util.Log;

import com.example.ephuizi.stl.R;
import com.example.ephuizi.stl.util.STLFile;
import com.example.ephuizi.stl.util.STLUtil;
import com.example.ephuizi.stl.util.ShaderHelper;

import java.util.Random;

/**
 * Created by ephuizi@gmail.com on 2016/1/7.
 */
public class GLPhongIllUminationDraw {

    private final int POSITION_DATA_SIZE = 3;
    private final int NORMAL_DATA_SIZE = 3;
    private final int BYTES_PER_FLOAT = 4;

    private MatrixPhongIlluminationSet mpis;
    private STLFile stlFile;
    private float zoomForNormalize;
    private float zoom;

    //
    //shader
    //
    private String vertexShader;
    private String fragmentShader;
    private int mProgramHandle;

    public GLPhongIllUminationDraw(GLSurfaceView glSurfaceView) {
        initShader(glSurfaceView);
        //TODO
        this.stlFile = STLUtil.parse("/sdcard/hui/test2.stl");
        this.mpis = new MatrixPhongIlluminationSet();

        float m = stlFile.absMax();
        this.zoomForNormalize = 2.0f / m;
        Log.e("info", "  this.zoomForNormalize " + this.zoomForNormalize);
        this.setZoom(zoomForNormalize);
    }

    //初始化shader
    public void initShader(GLSurfaceView glSurfaceView) {
        //加载顶点着色器的脚本内容
        vertexShader = ShaderHelper.loadSource(glSurfaceView.getContext(), R.raw.vertex_shader_phong_illumination);
        //加载片元着色器的脚本内容
        fragmentShader = ShaderHelper.loadSource(glSurfaceView.getContext(), R.raw.fragment_shader_phong_illumination);

        final int vertexShaderHandle = ShaderHelper.compileShader(GLES20.GL_VERTEX_SHADER, vertexShader);
        final int fragmentShaderHandle = ShaderHelper.compileShader(GLES20.GL_FRAGMENT_SHADER, fragmentShader);

        //基于顶点着色器与片元着色器创建程序
        mProgramHandle = ShaderHelper.createAndLinkProgram(vertexShaderHandle, fragmentShaderHandle,
                new String[]{"position", "normal", "lightPos", "model", "view", "projection", "viewPos", "lightColor", "objectColor", "normalMat"});
    }

    public void draw() {
        // Clear the colorbuffer
        GLES20.glClearColor(1f, 1f, 1f, 1.0f);
        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);

        // Set our per-vertex lighting program.
        GLES20.glUseProgram(mProgramHandle);

        // Set program handles for cube drawing.
        int positionLoc = GLES20.glGetAttribLocation(mProgramHandle, "position");
        int normalLoc = GLES20.glGetAttribLocation(mProgramHandle, "normal");

        int modelMatrixLoc = GLES20.glGetUniformLocation(mProgramHandle, "model");
        int viewMatrixLoc = GLES20.glGetUniformLocation(mProgramHandle, "view");
        int projectionMatrixLoc = GLES20.glGetUniformLocation(mProgramHandle, "projection");
        int normalMatrixLoc = GLES20.glGetUniformLocation(mProgramHandle, "normalMat");

        int lightPosLoc = GLES20.glGetUniformLocation(mProgramHandle, "lightPos");
        int viewPosLoc = GLES20.glGetUniformLocation(mProgramHandle, "viewPos");

        int lightColorLoc = GLES20.glGetUniformLocation(mProgramHandle, "lightColor");
        int objectColorLoc = GLES20.glGetUniformLocation(mProgramHandle, "objectColor");


        //
        //calculate
        //
        //TODO
        calculateModelMatrix();
        calculateViewMatrix();
        calculateNormalMatrix();

        //
        //draw
        //
        GLES20.glUniform3fv(lightPosLoc, 1, mpis.getLightPos(), 0);
        GLES20.glUniform3fv(viewPosLoc, 1, mpis.getViewPos(), 0);

        GLES20.glUniform3fv(lightColorLoc, 1, mpis.getLightColor(), 0);
        GLES20.glUniform3fv(objectColorLoc, 1, mpis.getObjectColor(), 0);

        GLES20.glUniformMatrix4fv(modelMatrixLoc, 1, false, mpis.getModelMatrix(), 0);
        GLES20.glUniformMatrix4fv(viewMatrixLoc, 1, false, mpis.getViewMatrix(), 0);
        GLES20.glUniformMatrix4fv(projectionMatrixLoc, 1, false, mpis.getProjectionMatrix(), 0);
        GLES20.glUniformMatrix4fv(normalMatrixLoc, 1, false, mpis.getNormalMatrix(), 0);

        //将顶点位置数据
        GLES20.glEnableVertexAttribArray(positionLoc);
        GLES20.glVertexAttribPointer
                (
                        positionLoc,
                        POSITION_DATA_SIZE,
                        GLES20.GL_FLOAT,
                        false,
                        POSITION_DATA_SIZE * BYTES_PER_FLOAT,
                        stlFile.getVertexBuffer()
                );
        ShaderHelper.checkGlError("positionLoc");

        GLES20.glEnableVertexAttribArray(normalLoc);
        GLES20.glVertexAttribPointer
                (
                        normalLoc,
                        NORMAL_DATA_SIZE,
                        GLES20.GL_FLOAT,
                        false,
                        NORMAL_DATA_SIZE * BYTES_PER_FLOAT,
                        stlFile.getNormalBuffer()
                );
        ShaderHelper.checkGlError("normalLoc");
        //绘制加载的物体
        GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, stlFile.getFacetNum() * 3);
        GLES20.glDisableVertexAttribArray(positionLoc);
        GLES20.glDisableVertexAttribArray(normalLoc);
    }

    private void calculateViewMatrix() {
        // Position the eye in front of the origin.
        final float eyeX = mpis.getViewPos()[0];
        final float eyeY = mpis.getViewPos()[1];
        final float eyeZ = mpis.getViewPos()[2];

        // We are looking toward the distance
        final float lookX = 0.0f;
        final float lookY = 0.0f;
        final float lookZ = 0.0f;

        // Set our up vector. This is where our head would be pointing were we holding the camera.
        final float upX = 0.0f;
        final float upY = 1.0f;
        final float upZ = 0.0f;

        // Set the view matrix. This matrix can be said to represent the camera position.
        // NOTE: In OpenGL 1, a ModelView matrix is used, which is a combination of a model and
        // view matrix. In OpenGL 2, we can keep track of these matrices separately if we choose.
        //gluLookAt(0.0,0.0,5.0, 0.0,0.0,0.0, 0.0,1.0,0.0); 相当于我们的脑袋位置在(0.0,0.0,5.0)处，眼睛望向(0.0,0.0,0.0),即原点。后面的三个参数(0.0,1.0,0.0),y轴为1，其余为0，表示脑袋朝上，就是正常的情况
        Matrix.setLookAtM(mpis.getViewMatrix(), 0, eyeX, eyeY, eyeZ, lookX, lookY, lookZ, upX, upY, upZ);
    }

    /**
     * 设置投影矩阵
     *
     * @param width  screen width
     * @param height screen height
     */
    public void calculateProjectionMatrix(int width, int height) {
        //
        final float ratio = (float) width / height;
        final float left = -ratio;
        final float right = ratio;
        final float bottom = -1.0f;
        final float top = 1.0f;
        final float near = 1.0f;
        final float far = 5.0f;

        Matrix.frustumM(mpis.getProjectionMatrix(), 0, left, right, bottom, top, near, far);
    }

    private void calculateModelMatrix() {
        // Translate the cube into the screen.
        Matrix.setIdentityM(mpis.getModelMatrix(), 0);
        Matrix.scaleM(mpis.getModelMatrix(), 0, getZoom(), getZoom(), getZoom());
        Matrix.translateM(mpis.getModelMatrix(), 0, -stlFile.centerX(), -stlFile.centerY(), -stlFile.centerZ());

//        // Set a matrix that contains the current rotation.
//        Matrix.setIdentityM(mCurrentRotation, 0);
//        Matrix.rotateM(mCurrentRotation, 0, mDeltaX, 0.0f, 1.0f, 0.0f);
//        Matrix.rotateM(mCurrentRotation, 0, mDeltaY, 1.0f, 0.0f, 0.0f);
//
//        // Multiply the current rotation by the accumulated rotation, and then set the accumulated rotation to the result.
//        Matrix.multiplyMM(mTemporaryMatrix, 0, mCurrentRotation, 0, mAccumulatedRotation, 0);
//        System.arraycopy(mTemporaryMatrix, 0, mAccumulatedRotation, 0, 16);
//
//        // Rotate the cube taking the overall rotation into account.
//        Matrix.multiplyMM(mTemporaryMatrix, 0, mModelMatrix, 0, mAccumulatedRotation, 0);
//        System.arraycopy(mTemporaryMatrix, 0, mModelMatrix, 0, 16);//update mModelMatrix

    }

    private void calculateNormalMatrix() {
        float[] temporaryMatrix = new float[16];
        Matrix.setIdentityM(temporaryMatrix, 0);
        Matrix.multiplyMM(temporaryMatrix, 0, mpis.getViewMatrix(), 0, mpis.getModelMatrix(), 0);
        // 法线模型变换矩阵 = 模型视图矩阵的逆转置矩阵
        Matrix.invertM(mpis.getNormalMatrix(), 0, mpis.getModelMatrix(), 0);
        Matrix.transposeM(mpis.getNormalMatrix(), 0, mpis.getNormalMatrix(), 0);
    }

    public float getZoom() {
        return zoom;
    }

    public void setZoom(float zoom) {
        this.zoom = zoom;
    }
}
